#ifndef XRPL_BASICS_HARDENED_HASH_H_INCLUDED
#define XRPL_BASICS_HARDENED_HASH_H_INCLUDED

#include <xrpl/beast/hash/hash_append.h>
#include <xrpl/beast/hash/xxhasher.h>

#include <cstdint>
#include <mutex>
#include <random>
#include <utility>

namespace ripple {

namespace detail {

using seed_pair = std::pair<std::uint64_t, std::uint64_t>;

template <bool = true>
seed_pair
make_seed_pair() noexcept
{
    struct state_t
    {
        std::mutex mutex;
        std::random_device rng;
        std::mt19937_64 gen;
        std::uniform_int_distribution<std::uint64_t> dist;

        state_t() : gen(rng())
        {
        }
        // state_t(state_t const&) = delete;
        // state_t& operator=(state_t const&) = delete;
    };
    static state_t state;
    std::lock_guard lock(state.mutex);
    return {state.dist(state.gen), state.dist(state.gen)};
}

}  // namespace detail

/**
 * Seed functor once per construction

   A std compatible hash adapter that resists adversarial inputs.
   For this to work, T must implement in its own namespace:

   @code

   template <class Hasher>
   void
   hash_append (Hasher& h, T const& t) noexcept
   {
       // hash_append each base and member that should
       //  participate in forming the hash
       using beast::hash_append;
       hash_append (h, static_cast<T::base1 const&>(t));
       hash_append (h, static_cast<T::base2 const&>(t));
       // ...
       hash_append (h, t.member1);
       hash_append (h, t.member2);
       // ...
   }

   @endcode

   Do not use any version of Murmur or CityHash for the Hasher
   template parameter (the hashing algorithm).  For details
   see https://131002.net/siphash/#at
*/

template <class HashAlgorithm = beast::xxhasher>
class hardened_hash
{
private:
    detail::seed_pair m_seeds;

public:
    using result_type = typename HashAlgorithm::result_type;

    hardened_hash() : m_seeds(detail::make_seed_pair<>())
    {
    }

    template <class T>
    result_type
    operator()(T const& t) const noexcept
    {
        HashAlgorithm h(m_seeds.first, m_seeds.second);
        hash_append(h, t);
        return static_cast<result_type>(h);
    }
};

}  // namespace ripple

#endif
